package org.apache.hadoop.contrib.mfs;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MemoryFileSystemCore {
    private static long chunkSize = -1;
    private static long chunkCount = -1;

    private byte[] chunkFlag;
    public final static byte NOT_ALLOCATED = 1;
    public final static byte ALLOCATED = 1 << 1;
    public final static byte UPDATING = 1 << 2; // why they are not consts??
    private ByteBuffer buf;
    private MetadataManager clsMetadat = MetadataManager.getInstance();

    public MemoryFileSystemCore() {
        /*int chunkCount = (int) (MetadataManager.getTotalStorageSize() / MemoryFileSystemCore.getChunkSize());
        chunkFlag = new byte[chunkCount];
        buf = new ByteBuffer();
        buf.allocateDirect(MetadataManager.getTotalStorageSize());*/

        chunkSize = clsMetadat.getChunkSize();
        chunkCount = (int)(clsMetadat.getTotalStorageSize() / chunkSize);
        buf = ByteBuffer.allocate((int)(chunkSize*chunkCount) ); //new byte[chunkSize*chunkCount];
        //ByteBuffer
        //buf.allocateDirect(10240*104857); // 1 Gigabytes default


        /* I think Almost all of Metadat can be stored in FirstChunk    */
    }

    public InputStream read(String path, int offset) throws FileNotFoundException{
        return read(path, offset, -1);
    }
    public InputStream read(String path, int Offset, int Length) throws FileNotFoundException
    {
        FileInfo fi = clsMetadat.find(path);
        if( fi == null ) throw new FileNotFoundException();
        synchronized(buf){
            if( Length == -1 ) Length = (int)fi.getSize();
            byte[] ret = new byte[Length];
            long FileChunkIndex = fi.getChunkList().get(0).seq; // 파일의 첫번째 청크의 인덱스

            int EarnedSize=0, EarnedTmp=0;
            while(true){
                EarnedTmp=0;

                buf.position((int)(FileChunkIndex*chunkSize));
                EarnedTmp = buf.getInt(); // EarnedTmp = Length

                if( EarnedTmp >= Length-EarnedSize ){
                    buf.get(ret, EarnedSize, Length-EarnedSize);
                    break;
                }else{
                    buf.get(ret, EarnedSize, EarnedTmp);
                }
                EarnedSize += EarnedTmp;
                if( buf.getInt((int)(FileChunkIndex*chunkSize+4)) != 0 ){
                    FileChunkIndex = buf.getInt((int)(FileChunkIndex*chunkSize+4));
                } else break;
            }
              /*
               * 4 Size of Chunk
               * 4 Next Index of Chunk
               */
            return new ByteArrayInputStream(ret);
        }
    }

    public void write(String path,byte[] bufwriting) throws InsufficientSpaceException
    {
        FileInfo fi;
        try {
            fi = clsMetadat.find(path);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(MemoryFileSystemCore.class.getName()).log(Level.SEVERE, null, ex);
            return;
        }
        
        
        synchronized(buf){
            clsMetadat.allocateChunks(chunkSize);
            long FileChunkIndex = fi.getChunkList().get((int)(fi.getChunkList().size()-1)).seq; //마지막 청크 인덱스
            int i;
            while(true){

            }
        }
    }

    public OutputStream update(String path)
    {
        return null;
    }

    public OutputStream append(String path)
    {
        return null;
    }

    public static long getChunkSize(){
        return chunkSize;
    }
    public void setChunkSize(int size){
        if(size > MetadataManager.getTotalStorageSize()){
            Logger.getLogger("MetadataManager").log(Level.WARNING, "ChunkSize exceeds TotalStorageSize");
            return;
        }
        chunkSize = size;
    }

    public boolean isAvailableStorage(){
        return (MetadataManager.getTotalStorageSize() / chunkSize) > 0 ? true : false;
    }

    public static int byteArrayToInt(byte[] b, int offset) {
        int value = 0;
        for (int i = 0; i < 4; i++) {
            int shift = (4 - 1 - i) * 8;
            value += (b[i + offset] & 0x000000FF) << shift;
        }
        return value;
    }

}